<?php
declare (strict_types=1);

namespace app\service;

use app\model\FetchLogModel;
use app\model\FetchTaskModel;
use app\model\FetchUrlModel;
use think\App;
use think\facade\Db;
use think\Exception;

class StorageService extends \think\Service
{
    function __construct(App $app, FetchTaskModel $fetchTaskModel, FetchLogModel $fetchLogModel, FetchUrlModel $fetchUrlModel)
    {
        $this->app = $app;
        $this->fetchTaskModel = $fetchTaskModel;
        $this->fetchLogModel = $fetchLogModel;
        $this->fetchUrlModel = $fetchUrlModel;
    }

    /**
     * 保存要读取过来的接口数据
     * @param $fetch_log_id 日志id
     * @param $remote_total 远程总数
     * @param $data 远程数据
     */
    function save($fetch_log_id, $remote_total, $rawdata)
    {
        $remote_total = intval($remote_total);
        $data = json_decode($rawdata, true);

        $objLog = $this->fetchLogModel->findOrFail($fetch_log_id);
        $objTask = $this->fetchTaskModel->findOrFail($objLog['fetch_task_id']);
        $objTask->inc('fetch_times')->update();
        $ojbUrl = $this->fetchUrlModel->findOrFail($objTask['fetch_url_id']);
        $ojbUrl->save(['remote_total' => $remote_total]); // 先更新远程总数,为以后分配任务准备
        $objLog->save(['raw_data' => $rawdata]);
        if (!$data and $remote_total) {
            $objLog->save(['is_rong_data' => 1]);
        }

        $rt = [];
        if (is_array($data) && $data) {
            $result_count = count($data);
            $table_name = $ojbUrl['table_name'];
            // step 1 修复解析数据
            $data = self::fixData($table_name, $data);
            $rs = self::parse($table_name, $data);

            $dataForSaveAll = $rs['data_for_save_all'];
            unset($rs['data_for_save_all']);
            $logData = $rs;
            // step 2 保存数据 保存成功后更新 finish_at 字段表示完成
            $rs['result_count'] = $result_count;

            $saveLog = $objLog->save($logData);// 后续处理错误这里先记录
            if ($dataForSaveAll) {
                try {
                    $saveOk = Db::name($table_name)->insertAll($dataForSaveAll);
                } catch (Exception $e) {
                    $saveOk = false;
                    $sql = Db::name($table_name)->getLastSql();
                    $msg = $e->getMessage();
                    file_put_contents(runtime_path() . 'msg_' . $fetch_log_id . '_error.log', $msg, FILE_APPEND);
                }
            } else {
                $saveOk = true;
            }

            // 不需要这样严格
            $hadSavedAll = $result_count == count($rs['id_saved_set']); // 已经保存了


            if ($saveOk or $hadSavedAll) {
                $finish_at = time();
                $data = ['finish_at' => $finish_at, 'result_count' => $result_count];
                $objTask->save($data);
                $objLog->save($data);
            } else {
                file_put_contents(runtime_path() . 'data_' . $fetch_log_id . '_error.log', var_export($dataForSaveAll, true));
            }

            $rt = $rs;
        } else {
            $data = ['finish_at' => 1, 'result_count' => 0];
            $objTask->save($data);
        }
        return $rt;
    }

    /**
     * 特殊记录处理
     * @param $string
     * @return string
     */
    static function ensure_utf8($string)
    {
        if (mb_check_encoding($string, 'UTF-8')) {
            // 字符串已经是 UTF-8 编码，直接返回
            return $string;
        } else {
            // 字符串不是 UTF-8 编码，尝试将其转换为 UTF-8 编码,
            $encoding = mb_detect_encoding($string, 'UTF-8, ISO-8859-1, ISO-8859-15, Windows-1252, GBK , Big5');
            if ($encoding === false) {
                // 无法检测到字符串的编码，丢弃该字符串
                return '';
            }
            $string = mb_convert_encoding($string, 'UTF-8', $encoding);
            return (string)$string;
        }
    }

    /**
     * @param $table_name
     * @param $data
     * @return array
     */
    static function fixData($table_name, $data)
    {
        foreach ($data as $row_id => $row) {
            foreach ($row as $field => $item) {
                if (is_array($item)) {
                    $data[$row_id][$field] = json_encode($item);
                }
            }
            // fix 下面比较特殊,遇到特殊记录
            if ('user' == $table_name) {
                $data[$row_id]['bio'] = self::ensure_utf8($row['bio']);
            }
        }
        return $data;
    }

    /**
     * 处理重复数据,并记录
     * @param $table_name
     * @param $data
     */
    static function parse($table_name, $data): array
    {
        // step 1 data=>[id=>row]
        $data = array_column($data, null, 'id');
        $idFetchedSet = array_keys($data);

        // step 2 获取保存的id [id=>id]
        $list = Db::name($table_name)->select($idFetchedSet);
        $id_saved_set = [];
        if ($list) {
            $id_saved_set = $list->column('id', 'id');
        }
        // step 3 解析 'max_id', 'min_id', 'debup_ids' 存日志 ;data 为去掉保存的的id,可以insertAll
        $max_id = max($idFetchedSet);
        $min_id = min($idFetchedSet);
        $data_for_save_all = array_diff_key($data, $id_saved_set);
        $dedup_ids = join(',', $id_saved_set);
        return compact('max_id', 'min_id', 'data_for_save_all', 'dedup_ids', 'id_saved_set');
    }
}
